# This file is sourced by all *interactive* bash shells on startup, including
# some apparently interactive shells such as scp and rcp that can't tolerate any
# output.  So make sure this doesn't display anything or bad things will happen.

# Test for an interactive shell. There is no need to set anything past this
# point for scp and rcp, and it's important to refrain from outputting anything
# in those cases.
if [[ $- != *i* ]] ; then
	# Shell is non-interactive. Be done now!
	return
fi

#------------------------------------------------------------------------------

# If not running tmux, attach to an existing session or launch one
if [[ $TERM == xterm* ]] ; then 
	tmux attach || tmux
fi

#------------------------------------------------------------------------------
# Enviroment variables
#------------------------------------------------------------------------------

EDITOR=vim

# Remove message from lesspipe.sh
export LESSQUIET=1

# Guard against npm messing with portage files.
# Also, allows 'npm install -g' without root.
export NPM_CONFIG_PREFIX=$HOME/.local/
export PATH="/home/$USER/go/bin:/home/$USER/.local/bin:$NPM_CONFIG_PREFIX/bin:$PATH"

# Add ~/.local/bin to PATH
export PATH="$HOME/.local/bin:$PATH"

# Ripgrep config path
export RIPGREP_CONFIG_PATH=~/.ripgreprc

# Android configuration
export ANDROID_HOME="${HOME}/.android-tooling"
export PATH="${PATH}:${ANDROID_HOME}/platform-tools";
export PATH="${PATH}:${ANDROID_HOME}/cmdline-tools/bin";

#------------------------------------------------------------------------------
# Aliases, functions and misc configurations
#------------------------------------------------------------------------------

# Aliases
#------------------------------------------------------------------------------

# Coreutils
alias ll='ls -l'
alias la='ls -a'
alias lla='ls -al'
alias egrep='grep -E'
alias fgrep='grep -F'
alias Pgrep='grep -P'

alias gad='git add . && git commit'

# Push and pull to notabug repository using passgit. Merely add 'push' or 'pull'
# to the end of string
alias notabug='passgit -n token_notabug'

# Use eman for showing manual pages
alias m='eman'

# Gentoo specific
alias update='doas emerge-webrsync && doas emerge -auD @world && doas emerge -c'
alias emerge-world='doas emerge -auD @world'
alias equ="equery u"
alias eqh="equery h"

# Start the X server from any tty as a normal user
alias startx="startx -- vt$(tty | sed -e 's|/dev/tty||')"

# Stow alias for handling dotfiles
alias dstow='stow --dotfiles'

# Dowload music playlists with format '$index-title.format'
alias dl_album='yt-dlp -f "ba" --audio-quality 10 -o "%(playlist_index)s-%(title)s.%(ext)s"'

# Opens mixer TUI for pulseaudio or alsa
alias mix='[[ $(type pulsemixer 2>/dev/null) ]] && pulsemixer || alsamixer'

# Misc
alias pdoc='perldoc'
alias e=$EDITOR

# Surfraw aliases
alias mr="sr marginalia"
alias yt="sr invidious"
alias go="sr google"
alias g="sr duckduckgo"

# Functions
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Simple functions
#------------------------------------------------------------------------------

# Fork zathura by default
zat() { zathura --fork "$@" & disown ;}

#-------------------------------------------------------------------------------
# Wrapper around some REPLS
# requires:
# 	- rlwrap
# Currently supported: perl, node
#-----------------------------------------------------------------------------

replwrap() { 
	! command -v rlwrap &>/dev/null &&\
		echo "${FUNCNAME[-1]}: rlwrap not found in PATH" &&\
		return 1
	
	local usage="$(cat <<-HELP_MSG
		Usage: repl [OPTION]
		Simple interface for launching some repls.

		perl	Launches a perl repl
		node	Launches a node repl
	HELP_MSG
	)"
	
	(($# != 1)) && echo "$usage" && return 1
	case "${1}" in
		perl)
			rlwrap -pBlack -A -S "$ " perl -wnE 'say eval()//$@'
			shift
		;;
		node)
			NODE_NO_READLINE=1 rlwrap node
			shift
		;;
		*) echo "${FUNCNAME[-1]}: Invalid option '$1'" && return 1 ;;
	esac
}

#-------------------------------------------------------------------------------
# Watch typescript files
# Args: files to test
# TODO: 
# 	- Try to find ts files to watch if none are supplied
#-----------------------------------------------------------------------------

watchts() {  
	if [[ -n "$@" ]] ; then
		compiled -s -c "make && node ${@//ts/js}"
	else
		echo "Please supply a file to be watched."
		return 1
	fi
}

#-------------------------------------------------------------------------------
# Open man pages as HTML inside elinks. Each page will be opened in its own tab.
#-------------------------------------------------------------------------------

eman() {
	# Dir we are storing the converted files
	local -r base_dir='/tmp/autoless'
	local -r convert='groff -spte -mandoc -Thtml'
	local -a pages_arr
	
	mkdir $base_dir 2>/dev/null

	# Treat arguments as potential pages
	for page in "$@" ; do
		# Find the path to the page and format its name
		local page=$(man -w $page)
		(($? != 0)) && continue # Skip missing pages

		# Format the page name
		local page_base=$(basename $page)
		local page_path=$base_dir/${page_base/%.*/.html}
		pages_arr+=($page_path)

		# Convert to HTML
		if [[ $page_base == *bz2* ]] ; then
			bzcat $page | $convert >${page_path} 2>/dev/null
		else
			# Handle uncompressed files
			$convert $page >${page_path} 2>/dev/null
		fi
	done

	elinks -force-html ${pages_arr[@]}

	# Cleaunp
	rm -rf $base_dir
}

#-------------------------------------------------------------------------------
# Shorten the PS1 directory names in the prompt.
# Note: This is needs to be hooked on the cd built-in and be called in main().
# TODO: Make it more portable, currently it hardcodes a style for the prompt.
#-----------------------------------------------------------------------------

shorten_PS1() {
	local shorten_depth=3
	local path="$(pwd)"
	[[ "$path" == *"$HOME"* ]] && path="~${path#$HOME}"
	depth="#${path//[^\/]}"
	depth="${#depth}"
	if [[ $depth -ge $shorten_depth ]] ; then
		newpath=""
		OLDIFS=$IFS
		IFS=/
		i=1
		for dir in $path ; do
			if [[ $i -eq $depth ]] ; then
				newpath+=$dir
				break
			fi
			char=${dir:0:1}
			[[ $char == '.' ]] && char+=${dir:1:1}
			newpath+=$char/
			((i++))
		done
		IFS=$OLDIFS
		path=$newpath
	fi
	PS1="\[\e[30;0;1m\]$path $\[\e[0m\] "
}

#-----------------------------------------------------------------------------
# Hack to silence autocd
# Note: Call silence_autocd in main()
#-----------------------------------------------------------------------------

silence_autocd() {
  exec {BASH_XTRACEFD}>/dev/null
}

# There are situations that it needs to be unsilenced
unsilence_autocd() {
  exec {BASH_XTRACEFD}>/dev/stdout
}

# Custom set
set() {
  # if calling "set -x", undo the silencing of autocd
  if [[ "$#" == 1 && "$1" == "-x" ]]; then
    command set -x;
    unsilence_autocd;
  elif [[ "$#" == 1 && "$1" == "+x" ]]; then
    silence_autocd;
    command set +x;
  else  
    command set "$@";
  fi;
}

#-----------------------------------------------------------------------------
# Changes to built-in commands
#-----------------------------------------------------------------------------

cd() {
	builtin cd $@
	shorten_PS1
	ls
}

# Completion set-up
#------------------------------------------------------------------------------

# doas completion
# Requires 'bash-completion'
complete -F _root_command doas

# Use 'man' completion on with 'eman' and 'm' alias
source /usr/share/bash-completion/completions/man
complete -F _comp_cmd_man eman m

#------------------------------------------------------------------------------
# Binding scripts
#------------------------------------------------------------------------------

# Surround
# TODO:
# 	- Error handling
#------------------------------------------------------------------------------

# Automatically surround text with passed characters. Meant to be bound to the
# character themselves.
# $1: 'all' or 'word'
# 	surround only a single word or all the text suceding
#   cursor.
# $2: opening character
# $3: closing character
_surround() {
	local line=$READLINE_LINE point=$READLINE_POINT
	local -r action=$1 open=$2 close=$3
	local -r start_to_cursor=${line:0:$point} cursor_to_end="${line:$point}"
	
	if [[ -z "$cursor_to_end" ]] ; then 
		line="$line$open$close"
		point=$((${#line} - 1))
	elif [[ "${line:$point:1}" == ' ' ]] ; then 
		line="$start_to_cursor$open$close$cursor_to_end"
		((point++))
	else
		case $action in
		all) 
			line="$start_to_cursor$open$cursor_to_end$close"
			point=$((${#line} - 1))
		;;
		word)
			local word=${cursor_to_end% *}
			line="$start_to_cursor${cursor_to_end/#$word/$open$word$close}"
			point=$(($point + ${#word} + 1))
		;;
		esac
	fi
	READLINE_LINE=$line
	READLINE_POINT=$point
}

# Bindings using the '_surround' function
bind -m vi-command -x '"(":"_surround all \( \)"'
bind -m vi-command -x '"\"":"_surround all \" \""'
bind -m vi-command -x "\"'\": \"_surround all \' \'\""
bind -m vi-command -x '"{":"_surround word \{ \}"'

#-----------------------------------------------------------------------------
# FZF
#-----------------------------------------------------------------------------

# Obsolete since version 0.48.0
#source /usr/share/fzf/{completion.bash,key-bindings.bash}

# Set up fzf key bindings and fuzzy completion
eval "$(fzf --bash)"

# Force __fzf_cd__ to use my modified cd (defined in .bashrc)
__fzf_cd__() {
  local dir
  dir=$(
    FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} \
    FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=dir,follow,hidden --scheme=path" "${FZF_ALT_C_OPTS-} +m
") \
    FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd)
  ) && printf 'cd -- %q' "$(builtin unset CDPATH && builtin cd -- "$dir" && builtin pwd)"
}

# Alt-c binding respects gitignore
export FZF_ALT_C_COMMAND="fd --ignore-file ~/.gitignore --hidden --follow --type directory"

# General styling. Open in a tmux popup, otherwise use heigh mode
export FZF_DEFAULT_OPTS='--height 40% --tmux bottom,40% --border top
	--color=fg:black,fg+:white,bg:#ffffd7,bg+:black
	--color=hl:black:bold,hl+:bright-blue,info:black,marker:white
	--color=prompt:black:regular,spinner:black,pointer:white,header:#727272
	--color=border:black,preview-label:black,label:black,query:black:regular
	--preview-window="border-sharp" --prompt=">" --marker=">" --pointer=">"
	--separator="─" --scrollbar="│" --layout="reverse"'

# Use fd to generate path
_fzf_compgen_path() {
  fd --ignore-file ~/.gitignore --hidden --follow --exclude ".git" . "$1"
}

# Use fd to generate directory list
 _fzf_compgen_dir() {
  fd --type d --ignore-file ~/.gitignore --hidden --follow --exclude ".git" . "$1"
}

# Commands that should work with **<TAB>
_fzf_setup_completion path mkdir vim zat mpv e

# Rebind fuzzy search on history 
bind -x '"\C-h": __fzf_history__'
bind -x '"\C-f": fzf_with_rg'

# Workaround Alt-C conflict in tmux
bind '"\ec": nop'
bind -m emacs-standard '"\C-r": " \C-b\C-k \C-u$(__fzf_cd__)\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"'
bind -m vi-command '"\C-r": "\C-z\C-r\C-z"'
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'

# Switch between Ripgrep mode and fzf filtering mode with C-f
fzf_with_rg() {
	rm -f /tmp/rg-fzf-{r,f}
	
	# Color customization is in ~/.ripgreprc
	local RG_PREFIX="rg --column --color=always "
	local INITIAL_QUERY="${*:-}"
	fzf --ansi --disabled --query "$INITIAL_QUERY" \
		--bind "start:reload:$RG_PREFIX {q}" \
		--bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
		--bind 'ctrl-f:transform:[[ ! $FZF_PROMPT == ripgrep* ]] &&
		  echo "rebind(change)+change-prompt(1. ripgrep> )+disable-search+transform-query:echo \{q} > /tmp/rg-fzf-f; cat /tmp/rg-fzf-r" ||
		  echo "unbind(change)+change-prompt(2. fzf> )+enable-search+transform-query:echo \{q} > /tmp/rg-fzf-r; cat /tmp/rg-fzf-f"' \
		--color "hl:-1:underline,hl+:-1:underline:reverse" \
		--prompt 'ripgrep> ' \
		--delimiter : \
		--header 'C-f: Switch between ripgrep/fzf' \
		--bind 'enter:become(vim {1} +{2})'
}

#-----------------------------------------------------------------------------
# Main
#-----------------------------------------------------------------------------

main() {
	silence_autocd
	shopt -s autocd
	shorten_PS1 # Shorten PS1 on startup
}
main
